#include <iostream>
#include <cstring>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main()
{
    //1、创建管道文件（属于OS），打开读写端
    const char* s="我是子进程，我正在给你发消息。";
    int fds[2];     
    //创建匿名管道，并打开两次，一写一读
    int n = pipe(fds);  //fds为输出型参数
    assert(n==0);

    //2、创建子进程
    pid_t id=fork();
    assert(id>=0);
    if(id==0)
    {
        //子进程，写
        close(fds[0]);  //关掉读
        char buf[1024];     //仅子进程看得见

        int cnt=0;
        while(1)
        {
            cnt++;
            snprintf(buf, sizeof buf, "%s[%d][%d]", s,cnt,getpid());
            //写入管道文件，该文件既不是父进程也不是子进程的，故不发生写时拷贝
            //写满管道文件时。会发生阻塞，等待读取
            n = write(fds[1], buf, strlen(buf));    
            assert(n>=0);
            sleep(1);
        }

        close(fds[1]);
        exit(0);
    }

    //父进程，读
    close(fds[1]);  //关掉写
    
    char buf[1024];
    while(1)
    {
        //从管道文件中读取，实际是拷贝一份，然后把缓冲区的数据设为无效（猜测是令size=0），然后新的数据可以覆盖其
        //如果管道中没有数据，读的时候，默认会阻塞等待当前正在读取的进程
        size_t num=read(fds[0],buf,sizeof(buf)-1);
        assert(num>=0);
        if(num>0) buf[num] = 0;     //主动添加\0
        cout<<"读取信息：# "<<buf<< " | my pid: "<<getpid()<<endl;
        //注意，父进程没有进行sleep

        close(fds[0]);
        cout<<"关闭读端文件描述符"<<endl;   //读端关闭，写也没意义，OS会给写发送信号，使其终止
        break;
    }

    int status;   //验证OS发终止信号
    n = waitpid(id,&status,0);
    assert(n>=0);

    cout<<"等待成功,子进程写结束:exit code:"<<((status>>8)&0xff) <<" signal:"<<(status&0x7f)<<endl;

    //close(fds[0]);

    //管道文件，文件描述符0 1 2被占用，3 4
    //[0]：读
    //[1]：写
    // cout<<"fds[0]: "<<fds[0]<<endl;
    // cout<<"fds[1]: "<<fds[1]<<endl;
    
    return 0;
}